Tutustu JavaScriptin asynkronisiin kontekstihaasteisiin ja hallitse säieturvallisuutta Node.js AsyncLocalStorage -ominaisuudella. Opas kontekstin eristämiseen vankkoihin, samanaikaisiin sovelluksiin.
JavaScriptin asynkroninen konteksti ja säieturvallisuus: Syväsukellus kontekstin eristämisen hallintaan
Nykyaikaisen ohjelmistokehityksen, erityisesti palvelinpuolen sovellusten, maailmassa tilanhallinta on perustavanlaatuinen haaste. Monisäikeisen pyyntömallin omaaville kielille säiekohtainen tallennustila tarjoaa yleisen ratkaisun datan eristämiseen säiekohtaisesti, pyyntökohtaisesti. Mutta mitä tapahtuu yksisäikeisessä, tapahtumasilmukkaan perustuvassa ympäristössä, kuten Node.js:ssä? Miten voimme turvallisesti hallita pyyntökohtaista kontekstia – kuten transaktiotunnusta, käyttäjäistuntoa tai lokalisointiasetuksia – monimutkaisen asynkronisten operaatioiden ketjun läpi ilman, että se vuotaa muihin samanaikaisiin pyyntöihin?
Tämä on asynkronisen kontekstinhallinnan ydinongelma. Sen ratkaisematta jättäminen johtaa sekavaan koodiin, tiukkaan kytkentään ja pahimmillaan katastrofaalisiin bugeihin, joissa yhden käyttäjän pyynnön tiedot saastuttavat toisen. Kyse on 'säieturvallisuuden' saavuttamisesta maailmassa ilman perinteisiä säikeitä.
Tämä kattava opas tutkii tämän ongelman kehitystä JavaScript-ekosysteemissä, tuskallisista manuaalisista kiertotavoista moderniin, vankkaan ratkaisuun, jota Node.js:n `AsyncLocalStorage` API tarjoaa. Pureudumme siihen, miten se toimii, miksi se on välttämätön skaalautuvien ja havaittavien järjestelmien rakentamisessa, ja miten sitä toteutetaan tehokkaasti omissa sovelluksissasi.
Haaste: Kadonnut konteksti asynkronisessa JavaScriptissä
Ymmärtääksemme ratkaisun täysin, meidän on ensin ymmärrettävä ongelma syvällisesti. JavaScriptin suoritusmalli perustuu yksisäikeiseen ja tapahtumasilmukkaan. Kun asynkroninen operaatio (kuten tietokantakysely, HTTP-kutsu tai `setTimeout`) aloitetaan, se siirretään erilliseen järjestelmään (kuten käyttöjärjestelmän ytimeen tai säiepooliin). JavaScript-säie voi vapaasti jatkaa muun koodin suorittamista. Kun asynkroninen operaatio valmistuu, takaisinkutsufunktio asetetaan jonoon, ja tapahtumasilmukka suorittaa sen, kun kutsujono on tyhjä.
Tämä malli on erittäin tehokas I/O-intensiivisille kuormituksille, mutta se luo merkittävän haasteen: suorituskonteksti katoaa asynkronisen operaation aloituksen ja sen takaisinkutsun suorituksen välillä. Takaisinkutsu suoritetaan tapahtumasilmukan uutena vuorona, irrallaan sitä aloittaneesta kutsujonosta.
Havainnollistetaan tätä yleisellä verkkopalvelin-skenaariolla. Kuvittelemme, että haluamme kirjata ainutlaatuisen `requestID`:n jokaiseen pyynnön elinkaaren aikana suoritettavaan toimintoon.
Naiivi lähestymistapa (ja miksi se epäonnistuu)
Kehittäjä, joka on uusi Node.js:ssä, saattaa yrittää käyttää globaalia muuttujaa:
let globalRequestID = null;
// Simuloitu tietokantakutsu
function getUserFromDB(userId) {
console.log(`[${globalRequestID}] Fetching user ${userId}`);
return new Promise(resolve => setTimeout(() => resolve({ id: userId, name: 'Jane Doe' }), 100));
}
// Simuloitu ulkoisen palvelun kutsu
async function getPermissions(user) {
console.log(`[${globalRequestID}] Getting permissions for ${user.name}`);
await new Promise(resolve => setTimeout(resolve, 150));
console.log(`[${globalRequestID}] Permissions retrieved`);
return { canEdit: true };
}
// Pääasiallinen pyyntökäsittelijän logiikka
async function handleRequest(requestID) {
globalRequestID = requestID;
console.log(`[${globalRequestID}] Starting request processing`);
const user = await getUserFromDB(123);
const permissions = await getPermissions(user);
console.log(`[${globalRequestID}] Request finished successfully`);
}
// Simuloidaan kahta samanaikaista pyyntöä, jotka saapuvat lähes samaan aikaan
console.log("Simulating concurrent requests...");
handleRequest('req-A');
handleRequest('req-B');
Jos suoritat tämän koodin, tulos on sotkuinen:
Simulating concurrent requests...
[req-A] Starting request processing
[req-A] Fetching user 123
[req-B] Starting request processing
[req-B] Fetching user 123
[req-B] Getting permissions for Jane Doe
[req-B] Getting permissions for Jane Doe
[req-B] Permissions retrieved
[req-B] Request finished successfully
[req-B] Permissions retrieved
[req-B] Request finished successfully
Huomaa, kuinka `req-B` ylikirjoittaa `globalRequestID`:n välittömästi. Siihen mennessä, kun `req-A`:n asynkroniset operaatiot jatkuvat, globaali muuttuja on muuttunut, ja kaikki seuraavat lokit on merkitty virheellisesti `req-B`:llä. Tämä on klassinen kilpailutilanne ja täydellinen esimerkki siitä, miksi globaali tila on tuhoisaa samanaikaisessa ympäristössä.
Tuskallinen kiertotapa: Prop drilling
Suorin ja kiistatta työläin ratkaisu on välittää konteksti-objekti jokaisen funktion läpi kutsujonossa. Tätä kutsutaan usein "prop drillingiksi".
// context on nyt eksplisiittinen parametri
function getUserFromDB(userId, context) {
console.log(`[${context.requestID}] Fetching user ${userId}`);
// ...
}
async function getPermissions(user, context) {
console.log(`[${context.requestID}] Getting permissions for ${user.name}`);
// ...
}
async function handleRequest(requestID) {
const context = { requestID };
console.log(`[${context.requestID}] Starting request processing`);
const user = await getUserFromDB(123, context);
const permissions = await getPermissions(user, context);
console.log(`[${context.requestID}] Request finished successfully`);
}
Tämä toimii. Se on turvallista ja ennakoitavaa. Sillä on kuitenkin merkittäviä haittoja:
- Boilerplate: Jokaisen funktion allekirjoituksen, ylätason kontrollerista matalimmalle tasolle ulottuvaan apuohjelmaan, on muutettava hyväksymään ja välittämään `context`-objekti.
- Tiukka kytkentä: Funktiot, jotka eivät itse tarvitse kontekstia, mutta ovat osa kutsujonoa, ovat pakotettuja tietämään siitä. Tämä rikkoo puhtaan arkkitehtuurin ja vastuunjakoperiaatteita.
- Virhealtis: Kehittäjän on helppo unohtaa välittää konteksti alas yhteen tasoon, mikä rikkoo ketjun kaikille myöhemmille kutsuille.
Vuosien ajan Node.js-yhteisö painiskeli tämän ongelman parissa, mikä johti erilaisiin kirjastopohjaisiin ratkaisuihin.
Edeltäjät ja varhaiset yritykset: Tie moderniin kontekstinhallintaan
Vanhentunut `domain`-moduuli
Node.js:n varhaiset versiot esittelivät `domain`-moduulin virheiden käsittelyyn ja I/O-operaatioiden ryhmittelyyn. Se sitoi implisiittisesti asynkroniset takaisinkutsut aktiiviseen "domainiin", joka saattoi myös sisältää kontekstidataa. Vaikka se vaikutti lupaavalta, sillä oli merkittäviä suorituskykykustannuksia ja se oli pahamaineisen epäluotettava, sisältäen hienovaraisia reunatapauksia, joissa konteksti saattoi kadota. Se vanhentui lopulta ja sitä ei tulisi käyttää nykyaikaisissa sovelluksissa.
Jatko-paikallinen tallennustila (CLS) -kirjastot
Yhteisö astui esiin konseptilla nimeltä "Jatko-paikallinen tallennustila". Kirjastot, kuten `cls-hooked`, tulivat erittäin suosituiksi. Ne toimivat hyödyntämällä Node:n sisäistä `async_hooks`-APIa, joka tarjoaa näkyvyyden asynkronisten resurssien elinkaareen.
Nämä kirjastot käytännössä paikkasivat tai "apinoivat" Node.js:n asynkronisia primitiivejä pitääkseen kirjaa nykyisestä kontekstista. Kun asynkroninen operaatio aloitettiin, kirjasto tallensi nykyisen kontekstin. Kun sen takaisinkutsu oli aikataulutettu suoritettavaksi, kirjasto palautti kyseisen kontekstin ennen takaisinkutsun suorittamista.
Vaikka `cls-hooked` ja vastaavat kirjastot olivat keskeisiä, ne olivat silti kiertotapoja. Ne luottivat sisäisiin API-rajapintoihin, jotka saattoivat muuttua, niillä saattoi olla omia suorituskykyvaikutuksiaan, ja ne kamppailivat joskus kontekstin oikean seuraamisen kanssa uudempien JavaScript-kielen ominaisuuksien, kuten `async/await`, kanssa, jos niitä ei konfiguroitu täydellisesti.
Moderni ratkaisu: `AsyncLocalStorage` esittely
Tunnustaen vakaan, ydinratkaisun kriittisen tarpeen, Node.js-tiimi esitteli `AsyncLocalStorage`-API:n. Se tuli vakaaksi Node.js v14:ssä ja on standardi, suositeltu tapa hallita asynkronista kontekstia tänään. Se hyödyntää samaa tehokasta `async_hooks`-mekanismia sisäisesti, mutta tarjoaa puhtaan, luotettavan ja suorituskykyisen julkisen API:n.
`AsyncLocalStorage` mahdollistaa eristetyn tallennuskontekstin luomisen, joka säilyy koko asynkronisten operaatioiden ketjun läpi, luoden tehokkaasti "pyyntökohtaisen" tallennustilan ilman prop drillingiä.
Ydin käsitteet ja menetelmät
`AsyncLocalStorage`:n käyttö pyörii muutaman keskeisen menetelmän ympärillä:
new AsyncLocalStorage(): Aloitat luomalla luokan instanssin. Tyypillisesti luot yhden instanssin tietylle kontekstityypille (esim. kaikille HTTP-pyynnöille) ja viet sen jaetusta moduulista..run(store, callback): Tämä on sisääntulopiste. Se ottaa kaksi argumenttia: `store` (data, jonka haluat tehdä saataville) ja `callback`-funktion. Se suorittaa takaisinkutsun välittömästi, ja koko sen synkronisen ja asynkronisen keston ajan, jonka takaisinkutsu suoritetaan, tarjottu `store` on saatavilla..getStore(): Näin haet datan. Kun kutsutaan funktiosta, joka on osa `.run()`:in aloittamaa asynkronista kulkua, se palauttaa kyseiseen kontekstiin liittyvän `store`-objektin. Jos sitä kutsutaan tällaisen kontekstin ulkopuolella, se palauttaa `undefined`.
Muokataan aikaisempaa esimerkkiä käyttämällä `AsyncLocalStorage`a.
const { AsyncLocalStorage } = require('async_hooks');
// 1. Luo yksi, jaettu instanssi
const asyncLocalStorage = new AsyncLocalStorage();
// 2. Funktiot eivät enää tarvitse 'context'-parametria
function getUserFromDB(userId) {
const store = asyncLocalStorage.getStore();
console.log(`[${store.requestID}] Fetching user ${userId}`);
return new Promise(resolve => setTimeout(() => resolve({ id: userId, name: 'Jane Doe' }), 100));
}
async function getPermissions(user) {
const store = asyncLocalStorage.getStore();
console.log(`[${store.requestID}] Getting permissions for ${user.name}`);
await new Promise(resolve => setTimeout(resolve, 150));
console.log(`[${store.requestID}] Permissions retrieved`);
return { canEdit: true };
}
async function businessLogic() {
const store = asyncLocalStorage.getStore();
console.log(`[${store.requestID}] Starting request processing`);
const user = await getUserFromDB(123);
const permissions = await getPermissions(user);
console.log(`[${store.requestID}] Request finished successfully`);
}
// 3. Pääasiallinen pyyntökäsittelijä käyttää .run() kontekstin luomiseen
function handleRequest(requestID) {
const context = { requestID };
asyncLocalStorage.run(context, () => {
// Kaikki täältä kutsutut, synkronisesti tai asynkronisesti, pääsevät kontekstiin
businessLogic();
});
}
console.log("Simulating concurrent requests with AsyncLocalStorage...");
handleRequest('req-A');
handleRequest('req-B');
Tulos on nyt täydellisen oikea ja eristetty:
Simulating concurrent requests with AsyncLocalStorage...
[req-A] Starting request processing
[req-A] Fetching user 123
[req-B] Starting request processing
[req-B] Fetching user 123
[req-A] Getting permissions for Jane Doe
[req-B] Getting permissions for Jane Doe
[req-A] Permissions retrieved
[req-A] Request finished successfully
[req-B] Permissions retrieved
[req-B] Request finished successfully
Huomaa puhdas erottelu. Funktiot `getUserFromDB` ja `getPermissions` ovat puhtaita; niissä ei ole `context`-parametria. Ne voivat yksinkertaisesti pyytää kontekstia, kun tarvitsevat sitä `getStore()`-metodin kautta. Konteksti luodaan kerran pyynnön sisääntulopisteessä (`handleRequest`) ja se kulkee implisiittisesti koko asynkronisen ketjun läpi.
Käytännön toteutus: Todellinen esimerkki Express.js:n kanssa
Yksi tehokkaimmista `AsyncLocalStorage`:n käyttötapauksista on verkkokehysten, kuten Express.js:n, kanssa pyyntökohtaisen kontekstin hallintaan. Rakennetaan käytännön esimerkki.
Skenaario
Meillä on verkkosovellus, jonka täytyy:
- Määrittää jokaiselle saapuvalle pyynnölle ainutlaatuinen `requestID` jäljitettävyyttä varten.
- Olla keskitetty lokipalvelu, joka automaattisesti sisällyttää tämän `requestID`:n jokaiseen lokiviestiin ilman manuaalista välittämistä.
- Tehdä käyttäjätiedot saataville myöhemmille palveluille todennuksen jälkeen.
Vaihe 1: Luo keskitetty kontekstipalvelu
On parasta luoda yksi moduuli, joka hallinnoi `AsyncLocalStorage`-instanssia.
Tiedosto: `context.js`
const { AsyncLocalStorage } = require('async_hooks');
// Tämä instanssi jaetaan koko sovelluksen kesken
const requestContext = new AsyncLocalStorage();
module.exports = { requestContext };
Vaihe 2: Luo väliohjelma kontekstin luomiseksi
Expressissä väliohjelma on täydellinen paikka käyttää `.run()` -metodia koko pyynnön elinkaaren kietomiseen.
Tiedosto: `app.js` (tai pääpalvelintiedosto)
const express = require('express');
const { v4: uuidv4 } = require('uuid');
const { requestContext } = require('./context');
const logger = require('./logger');
const userService = require('./userService');
const app = express();
// Väliohjelma asynkronisen kontekstin luomiseksi jokaiselle pyynnölle
app.use((req, res, next) => {
const store = {
requestID: uuidv4(),
user: null // Täytetään todennuksen jälkeen
};
// .run() kietoo loput pyynnön käsittelystä (next())
requestContext.run(store, () => {
logger.info(`Request started: ${req.method} ${req.url}`);
next();
});
});
// Simuloitu todennusväliohjelma
app.use((req, res, next) => {
// Todellisessa sovelluksessa varmistaisit tokenin tässä
const store = requestContext.getStore();
if (store) {
store.user = { id: 'user-123', name: 'Alice' };
}
next();
});
// Sovelluksesi reitit
app.get('/user', async (req, res) => {
logger.info('Handling /user request');
try {
const userProfile = await userService.getProfile();
res.json(userProfile);
} catch (error) {
logger.error('Failed to get user profile', { error: error.message });
res.status(500).send('Internal Server Error');
}
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
Vaihe 3: Lokitusohjelma, joka käyttää automaattisesti kontekstia
Tässä tapahtuu taika. Lokitusohjelmamme voi olla täysin tietämätön Expressistä, pyynnöistä tai käyttäjistä. Se tuntee vain keskitetyn kontekstipalvelumme.
Tiedosto: `logger.js`
const { requestContext } = require('./context');
function log(level, message, details = {}) {
const store = requestContext.getStore();
const requestID = store ? store.requestID : 'N/A';
const logObject = {
timestamp: new Date().toISOString(),
level: level.toUpperCase(),
requestID,
message,
...details
};
console.log(JSON.stringify(logObject));
}
const logger = {
info: (message, details) => log('info', message, details),
error: (message, details) => log('error', message, details),
warn: (message, details) => log('warn', message, details),
};
module.exports = logger;
Vaihe 4: Syvälle sisäkkäinen palvelu, joka käyttää kontekstia
Nyt `userService` voi luottavaisesti käyttää pyyntökohtaista tietoa ilman parametrien välittämistä kontrollerista alaspäin.
Tiedosto: `userService.js`
const { requestContext } = require('./context');
const logger = require('./logger');
// Simuloitu tietokantakutsu
async function fetchUserDetailsFromDB(userId) {
logger.info(`Fetching details for user ${userId} from database.`);
await new Promise(resolve => setTimeout(resolve, 50));
return { company: 'Global Tech Inc.', country: 'Worldwide' };
}
async function getProfile() {
const store = requestContext.getStore();
if (!store || !store.user) {
throw new Error('User not authenticated');
}
logger.info(`Building profile for user: ${store.user.name}`);
// Jopa syvemmät asynkroniset kutsut säilyttävät kontekstin
const details = await fetchUserDetailsFromDB(store.user.id);
return {
id: store.user.id,
name: store.user.name,
...details
};
}
module.exports = { getProfile };
Kun käynnistät tämän palvelimen ja teet pyynnön osoitteeseen `http://localhost:3000/user`, konsolilokit näyttävät selvästi, että sama `requestID` on läsnä jokaisessa lokiviestissä, alkaen alkuperäisestä väliohjelmasta syvimmän tietokantatoiminnon tasolle, osoittaen täydellistä kontekstin eristämistä.
Säieturvallisuus ja kontekstin eristäminen selitettynä
Nyt voimme palata takaisin "säieturvallisuus"-termiin. Node.js:ssä huoli ei ole useiden säikeiden samanaikaisesta pääsystä samaan muistiin todellisessa rinnakkaisessa muodossa. Sen sijaan kyse on useiden samanaikaisten operaatioiden (pyyntöjen) suoritusten lomittumisesta pääsäikeeseen tapahtumasilmukan kautta. "Turvallisuusongelma" on varmistaa, että yhden operaation konteksti ei vuoda toiseen.
`AsyncLocalStorage` saavuttaa tämän linkittämällä kontekstin asynkronisiin resursseihin.
Tässä yksinkertaistettu mielikuva siitä, mitä tapahtuu:
- Kun `asyncLocalStorage.run(store, ...)` kutsutaan, Node.js sanoo sisäisesti: "Olen nyt astumassa erityiseen kontekstiin. Tämän kontekstin tiedot ovat `store`." Se määrittää tälle suorituskontekstille ainutlaatuisen sisäisen tunnuksen.
- Mikä tahansa asynkroninen operaatio, joka ajoitetaan tämän kontekstin ollessa aktiivinen (esim. `new Promise`, `setTimeout`, `fs.readFile`), merkitään tällä ainutlaatuisella kontekstitunnuksella.
- Myöhemmin, kun tapahtumasilmukka poimii takaisinkutsun jollekin näistä merkityistä operaatioista, Node.js tarkistaa tunnuksen. Se sanoo: "Ahaa, tämä takaisinkutsu kuuluu kontekstitunnukseen X. Palautan nyt kyseisen kontekstin ennen takaisinkutsun suorittamista."
- Tämä palautus tekee oikean `store`:n saataville `getStore()`-metodille takaisinkutsun sisällä.
- Kun toinen pyyntö saapuu, sen `.run()`-kutsu luo täysin uuden kontekstin eri sisäisellä tunnuksella, ja sen asynkroniset operaatiot merkitään tällä uudella tunnuksella, varmistaen nollan päällekkäisyyden.
Tämä vankka, matalan tason mekanismi varmistaa, että riippumatta siitä, miten tapahtumasilmukka lomittaa eri pyyntöjen takaisinkutsujen suoritusta, `getStore()` palauttaa aina tiedot kontekstista, johon kyseisen takaisinkutsun asynkroninen operaatio alun perin ajoitettiin.
Suorituskykyyn liittyvät näkökohdat ja parhaat käytännöt
Vaikka `AsyncLocalStorage` on erittäin optimoitu, se ei ole ilmaista. Taustalla olevat `async_hooks`-mekanismit lisäävät pienen määrän ylikuormitusta jokaisen asynkronisen resurssin luomiseen ja päättymiseen. Useimmissa sovelluksissa, erityisesti I/O-intensiivisissä, tämä ylikuormitus on kuitenkin merkityksetön verrattuna koodin selkeyteen, ylläpidettävyyteen ja havaittavuuteen.
- Instansioi kerran: Luo `AsyncLocalStorage`-instanssit sovelluksesi ylätasolle ja käytä niitä uudelleen. Älä luo uusia instansseja pyyntöä kohden.
- Pidä store kevyenä: Kontekstivarasto ei ole välimuisti. Käytä sitä pienille, välttämättömille tiedoille, kuten tunnuksille, tokeneille tai kevyille käyttäjäobjekteille. Vältä suurten tietopakettien tallentamista.
- Luo konteksti selkeissä sisääntulopisteissä: Parhaat paikat `.run()`-kutsuille ovat riippumattoman asynkronisen kulun selkeät aloituskohdat. Näitä ovat palvelimen pyyntöjen väliohjelmat, viestijonojen kuluttajat tai työn ajastimet.
- Ole tietoinen "fire-and-forget"-operaatioista: Jos aloitat asynkronisen operaation `run`-kontekstin sisällä, mutta et `await` sitä (esim. `doSomething().catch(...)`), se perii silti kontekstin oikein. Tämä on tehokas ominaisuus taustatehtäville, jotka on jäljitettävä alkuperäänsä.
- Ymmärrä sisäkkäisyys: Voit sisäkkäin kutsua `.run()`-metodia. `.run()`-metodin kutsuminen olemassa olevan kontekstin sisältä luo uuden, sisäkkäisen kontekstin. `getStore()` palauttaa tällöin sisimmän storen. Tämä voi olla hyödyllistä kontekstin väliaikaiseen ylikirjoittamiseen tai lisäämiseen tiettyä ala-operaatiota varten.
JavaScriptin ulkopuolella: Tulevaisuus `AsyncContext`:lla
Asynkronisen kontekstinhallinnan tarve ei rajoitu Node.js:ään. Tunnistaen sen tärkeyden koko JavaScript-ekosysteemille, muodollinen ehdotus nimeltä `AsyncContext` on matkalla TC39-komiteaan, joka standardoi JavaScriptiä (ECMAScript).
`AsyncContext`-ehdotus on vahvasti saanut inspiraationsa Node.js:n `AsyncLocalStorage`-ominaisuudesta ja sen tavoitteena on tarjota lähes identtinen API, joka olisi saatavilla kaikissa moderneissa JavaScript-ympäristöissä, mukaan lukien web-selaimet. Tämä voisi avata tehokkaita ominaisuuksia etupään kehitykseen, kuten kontekstin hallintaan monimutkaisissa kehyksissä, kuten Reactissa, samanaikaisen renderöinnin aikana, tai käyttäjän vuorovaikutusvirtojen seuraamiseen monimutkaisissa komponenttipuissa.
Johtopäätös: Deklaratiivisen ja vankan asynkronisen koodin omaksuminen
Tilan hallinta asynkronisten operaatioiden läpi on petollisen monimutkainen ongelma, joka on haastanut JavaScript-kehittäjiä vuosia. Matka manuaalisesta prop drillingistä ja hauraista yhteisö-kirjastoista ydin-, vakaaseen API:in, kuten `AsyncLocalStorage`, merkitsee merkittävää kypsymistä Node.js-alustalla.
Tarjoamalla mekanismin turvalliseen, eristettyyn ja implisiittisesti levitettyyn kontekstiin, `AsyncLocalStorage` mahdollistaa puhtaamman, paremmin irrotetun ja helpommin ylläpidettävän koodin kirjoittamisen. Se on kulmakivi modernien, havaittavien järjestelmien rakentamisessa, joissa jäljitys, valvonta ja lokitus eivät ole jälkikäteen mietittyjä asioita, vaan kudottuina sovelluksen rakenteeseen.
Jos rakennat mitään ei-triviaalia Node.js-sovellusta, joka käsittelee samanaikaisia operaatioita, `AsyncLocalStorage`:n omaksuminen ei ole enää vain paras käytäntö – se on perustavanlaatuinen tekniikka vankkuuden ja skaalautuvuuden saavuttamiseksi asynkronisessa maailmassa.